#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#

if (NOT ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX)
    SET(${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FILEPATH "Custom path to install python bindings.")
endif()

message(STATUS "PYTHON INSTALL PREFIX ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}")

if (WIN32)
    set(PATH_SEP "\;")
else()
    set(PATH_SEP ":")
endif()
if (NOT CMAKE_CXX_STANDARD)
    set(CMAKE_CXX_STANDARD 17)
endif()

# On macOS, check if Qt is a framework build. This affects how include paths should be handled.
get_target_property(QtCore_is_framework Qt5::Core FRAMEWORK)
if (QtCore_is_framework)
    # Get the path to the framework dir.
    list(GET Qt5Core_INCLUDE_DIRS 0 QT_INCLUDE_DIR)
    get_filename_component(QT_FRAMEWORK_INCLUDE_DIR "${QT_INCLUDE_DIR}/../" ABSOLUTE)

    # QT_INCLUDE_DIR points to the QtCore.framework directory, so we need to adjust this to point
    # to the actual include directory, which has include files for non-framework parts of Qt.
    get_filename_component(QT_INCLUDE_DIR "${QT_INCLUDE_DIR}/../../include" ABSOLUTE)
endif()

# Flags that we will pass to shiboken-generator
# --generator-set=shiboken:  tells the generator that we want to use shiboken to generate code,
#                            a doc generator is also available
# --enable-parent-ctor-heuristic: Enable heuristics to detect parent relationship on constructors,
#                           this try to guess parent ownership based on the arguments of the constructors
# --enable-pyside-extensionsL: This will generate code for Qt based classes, adding extra attributes,
#                           like signal, slot;
# --enable-return-value-heuristic: Similar as --enable-parent-ctor-heuristic this use some logic to guess
#                           parent child relationship based on the returned argument
# --use-isnull-as-nb_nonzero: If a class have an isNull() const method, it will be used to compute
#                           the value of boolean casts.
#                           Example, QImage::isNull() will be used when on python side you do `if (myQImage)`
set(GENERATOR_EXTRA_FLAGS --generator-set=shiboken
                          --enable-parent-ctor-heuristic
                          --enable-pyside-extensions
                          --enable-return-value-heuristic
                          --use-isnull-as-nb_nonzero
                          -std=c++${CMAKE_CXX_STANDARD})

# 2017-04-24 The protected hack can unfortunately not be disabled, because
# Clang does produce linker errors when we disable the hack.
# But the ugly workaround in Python is replaced by a shiboken change.
if(WIN32 OR DEFINED AVOID_PROTECTED_HACK)
    set(GENERATOR_EXTRA_FLAGS ${GENERATOR_EXTRA_FLAGS} --avoid-protected-hack)
    add_definitions(-DAVOID_PROTECTED_HACK)
endif()

macro(make_path varname)
   # accepts any number of path variables
   string(REPLACE ";" "${PATH_SEP}" ${varname} "${ARGN}")
endmacro()

# Creates a PySide module target based on the arguments
# This will:
# 1 - Create a Cmake custom-target that call shiboken-generator passign the correct arguments
# 2 - Create a Cmake library target called "Py${LIBRARY_NAME}" the output name of this target
#     will be changed to match PySide template
# Args:
#   LIBRARY_NAME - The name of the output module
#   TYPESYSTEM_PATHS - A list of paths where shiboken should look for typesystem files
#   INCLUDE_PATHS - Include pahts necessary to parse your class. *This is not the same as build*
#   OUTPUT_SOURCES - The files that will be generated by shiboken
#   TARGET_INCLUDE_DIRS - This will be passed to target_include_directories
#   TARGET_LINK_LIBRARIES - This will be passed to target_link_libraries
#   GLOBAL_INCLUDE - A header-file that contains alls classes that will be generated
#   TYPESYSTEM_XML - The target binding typesystem (that should be the full path)
#   DEPENDS - This var will be passed to add_custom_command(DEPENDS) so a new generation will be
#             trigger if one of these files changes
#   MODULE_OUTPUT_DIR - Where the library file should be stored
macro(CREATE_PYTHON_BINDINGS
        LIBRARY_NAME
        TYPESYSTEM_PATHS
        INCLUDE_PATHS
        OUTPUT_SOURCES
        TARGET_INCLUDE_DIRS
        TARGET_LINK_LIBRARIES
        GLOBAL_INCLUDE
        TYPESYSTEM_XML
        DEPENDS
        MODULE_OUTPUT_DIR)

    # Transform the path separators into something shiboken understands.
    make_path(shiboken_include_dirs ${INCLUDE_PATHS})
    make_path(shiboken_typesystem_dirs ${TYPESYSTEM_PATHS})
    get_property(raw_python_dir_include_dirs DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
    make_path(python_dir_include_dirs ${raw_python_dir_include_dirs})
    set(shiboken_include_dirs "${shiboken_include_dirs}${PATH_SEP}${python_dir_include_dirs}")

    set(shiboken_framework_include_dirs_option "")
    if(CMAKE_HOST_APPLE)
        set(shiboken_framework_include_dirs "${QT_FRAMEWORK_INCLUDE_DIR}")
        make_path(shiboken_framework_include_dirs ${shiboken_framework_include_dirs})
        set(shiboken_framework_include_dirs_option "--framework-include-paths=${shiboken_framework_include_dirs}")
    endif()
    set_property(SOURCE ${OUTPUT_SOURCES} PROPERTY SKIP_AUTOGEN ON)
    add_custom_command(OUTPUT ${OUTPUT_SOURCES}
        COMMAND $<TARGET_PROPERTY:Shiboken2::shiboken,LOCATION> ${GENERATOR_EXTRA_FLAGS}
                ${GLOBAL_INCLUDE}
                --include-paths=${shiboken_include_dirs}
                --typesystem-paths=${shiboken_typesystem_dirs}
                ${shiboken_framework_include_dirs_option}
                --output-directory=${CMAKE_CURRENT_BINARY_DIR}
                ${TYPESYSTEM_XML}
        DEPENDS ${TYPESYSTEM_XML} ${DEPENDS}
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        COMMENT "Running generator for ${LIBRARY_NAME} binding...")

        set(TARGET_NAME "Py${LIBRARY_NAME}")
        set(MODULE_NAME "${LIBRARY_NAME}")
        add_library(${TARGET_NAME} MODULE ${OUTPUT_SOURCES})

        set_target_properties(${TARGET_NAME} PROPERTIES
            PREFIX ""
            OUTPUT_NAME ${MODULE_NAME}
            LIBRARY_OUTPUT_DIRECTORY ${MODULE_OUTPUT_DIR}
        )

        if(WIN32)
            set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".pyd")
        endif()

        target_include_directories(${TARGET_NAME} PUBLIC
            ${TARGET_INCLUDE_DIRS}
            ${PYSIDE_EXTRA_INCLUDES}
        )

        target_link_libraries(${TARGET_NAME}
            ${TARGET_LINK_LIBRARIES}
            PySide2::pyside2
            Shiboken2::libshiboken
        )
        target_compile_definitions(${TARGET_NAME}
            PRIVATE Py_LIMITED_API=0x03050000
        )
        if(APPLE)
            set_property(TARGET ${TARGET_NAME} APPEND PROPERTY
                LINK_FLAGS "-undefined dynamic_lookup")
        endif()
        install(TARGETS ${TARGET_NAME}
            LIBRARY DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/${TARGET_NAME})
endmacro()
